/*
 * Pool.h
 *
 *  Created on: 2013. 8. 28.
 *      Author: jjaehuny
 */

#ifndef PROJECTZ_NUTIL_POOL__EXCEPTION_H_
#define PROJECTZ_NUTIL_POOL__EXCEPTION_H_

#include <list>
#include <algorithm>
#include <mutex>
#include <typeinfo>
#include "../macro.h"

namespace ProjectZ { namespace NUtil {

//template <typename T> void del_ptr(T& t) { delete t; }
//template <typename T> void del_second_ptr(T& t) { delete t.second; }
//template <typename T> void Finalize_ptr(T& t) { t->Finalize(); }

template<class Type>
class Pool_Exception
{
public:
	Pool_Exception(int nSize);
	~Pool_Exception();

	Type * 	NEW();
	void 		DEL(Type * pType);

	Type * 	SAFE_NEW();
	void 		SAFE_DEL(Type * pType);

private:
	std::mutex				_clsLock;
	std::list<Type *>		_clsTypeList;

	int 					nPostMakeCount;
	int 					nNewMakeCount;
};

template<class Type>
Pool_Exception<Type>::Pool_Exception(int nSize)
{
	nPostMakeCount = 0;
	nNewMakeCount = 0;

	for (int i = 0; i < nSize; i++)
	{
		_clsTypeList.push_back(new Type);

		nPostMakeCount++;
		if (0 == nPostMakeCount % 1000)
		{
			DEBUGLOG("Make Type : %s Post Make Count : %d", typeid(Type).name(), nPostMakeCount);
		}
	}
}

template<class Type>
Pool_Exception<Type>::~Pool_Exception()
{
	std::for_each(_clsTypeList.begin(), _clsTypeList.end(), del_ptr<Type*>);
	_clsTypeList.clear();
}

template<class Type>
Type *
Pool_Exception<Type>::NEW()
{
	Type * pType = NULL;

	if (true == _clsTypeList.empty())
	{
		pType = new Type;

		nNewMakeCount++;
		if (0 == nNewMakeCount % 1000)
		{
			DEBUGLOG("Make New Type : %s Count : %d", typeid(Type).name(), nNewMakeCount);
		}
	}
	else
	{
		pType = _clsTypeList.front();
		_clsTypeList.pop_front();
//		DEBUGLOG("NEW : %s, Count=%d", typeid(Type).name(), _clsTypeList.size());
	}

	pType->Initialize();
	return pType;
}

template<class Type>
void
Pool_Exception<Type>::DEL(Type * pType)
{
	if (0 >= pType->GetRefCount())
	{
		DEBUGLOG("Pool_Exception Type : %s Count : %d", typeid(Type).name(), pType->GetRefCount());
		return;
	}

	pType->Finalize();
	_clsTypeList.push_back(pType);
//	DEBUGLOG("DEL : %s, Count=%d", typeid(Type).name(), _clsTypeList.size());
}

template<class Type>
Type *
Pool_Exception<Type>::SAFE_NEW()
{
	Type * pType = NULL;
	{
		std::lock_guard<std::mutex> lock(_clsLock);
		if (true == _clsTypeList.empty())
		{
			pType = new Type;

			nNewMakeCount++;
			if (0 == nNewMakeCount % 1000)
			{
				DEBUGLOG("Make New Type : %s Count : %d", typeid(Type).name(), nNewMakeCount);
			}
		}
		else
		{
			pType = _clsTypeList.front();
			_clsTypeList.pop_front();
		}
	}

	pType->Initialize();
	return pType;
}

template<class Type>
void
Pool_Exception<Type>::SAFE_DEL(Type * pType)
{
	if (0 >= pType->GetRefCount())
	{
		DEBUGLOG("Pool_Exception Type : %s Count : %d", typeid(Type).name(), pType->GetRefCount());
		return;
	}

	{
		std::lock_guard<std::mutex> lock(_clsLock);
		_clsTypeList.push_back(pType);
	}
}

} /*NUtil*/ } /*ProjectZ*/

#endif /* PROJECTZ_NUTIL_POOL__EXCEPTION_H_ */
